//! 其他指令编码模块
//! 
//! 实现 8086 其他指令：NOP, INT, 标志位操作, IO指令

use crate::ast::Operand;
use crate::error::{AssemblerError, Result};

/// 其他指令编码器
pub struct OtherEncoder;

impl OtherEncoder {
    /// 编码其他指令
    pub fn encode(&self, instruction: &str, operands: &[Operand]) -> Result<Vec<u8>> {
        match instruction {
            "空操作" => self.encode_nop(operands),
            "中断" => self.encode_int(operands),
            // 标志位操作指令
            "清除进位" => self.encode_clc(operands),
            "设置进位" => self.encode_stc(operands),
            "取反进位" => self.encode_cmc(operands),
            "清除方向" => self.encode_cld(operands),
            "设置方向" => self.encode_std(operands),
            "清除中断" => self.encode_cli(operands),
            "设置中断" => self.encode_sti(operands),
            // IO指令
            "输入" => self.encode_in(operands),
            "输出" => self.encode_out(operands),
            _ => Err(AssemblerError::CodeGenError {
                message: format!("未知的其他指令: {}", instruction),
            }),
        }
    }

    /// 编码空操作指令
    fn encode_nop(&self, operands: &[Operand]) -> Result<Vec<u8>> {
        if !operands.is_empty() {
            return Err(AssemblerError::OperandError {
                message: "空操作 指令不需要操作数".to_string(),
                line: 0,
            });
        }
        Ok(vec![0x90])
    }

    /// 编码中断指令（INT）
    fn encode_int(&self, operands: &[Operand]) -> Result<Vec<u8>> {
        if operands.len() != 1 {
            return Err(AssemblerError::OperandError {
                message: "中断 指令需要 1 个操作数".to_string(),
                line: 0,
            });
        }

        if let Operand::Immediate(imm) = &operands[0] {
            // 检查中断号范围（0-255）
            if *imm < 0 || *imm > 255 {
                return Err(AssemblerError::OperandError {
                    message: format!("中断号必须在 0-255 范围内，得到: {}", imm),
                    line: 0,
                });
            }
            
            // INT 指令：操作码 0xCD + 中断号（8位）
            Ok(vec![0xCD, *imm as u8])
        } else {
            Err(AssemblerError::OperandError {
                message: "中断 指令只支持立即数操作数".to_string(),
                line: 0,
            })
        }
    }

    /// 编码清除进位标志指令（CLC）
    /// 
    /// CLC: 清除进位标志（CF = 0）
    /// 操作码: 0xF8
    fn encode_clc(&self, operands: &[Operand]) -> Result<Vec<u8>> {
        if !operands.is_empty() {
            return Err(AssemblerError::OperandError {
                message: "清除进位 指令不需要操作数".to_string(),
                line: 0,
            });
        }
        Ok(vec![0xF8])
    }

    /// 编码设置进位标志指令（STC）
    /// 
    /// STC: 设置进位标志（CF = 1）
    /// 操作码: 0xF9
    fn encode_stc(&self, operands: &[Operand]) -> Result<Vec<u8>> {
        if !operands.is_empty() {
            return Err(AssemblerError::OperandError {
                message: "设置进位 指令不需要操作数".to_string(),
                line: 0,
            });
        }
        Ok(vec![0xF9])
    }

    /// 编码取反进位标志指令（CMC）
    /// 
    /// CMC: 取反进位标志（CF = !CF）
    /// 操作码: 0xF5
    fn encode_cmc(&self, operands: &[Operand]) -> Result<Vec<u8>> {
        if !operands.is_empty() {
            return Err(AssemblerError::OperandError {
                message: "取反进位 指令不需要操作数".to_string(),
                line: 0,
            });
        }
        Ok(vec![0xF5])
    }

    /// 编码清除方向标志指令（CLD）
    /// 
    /// CLD: 清除方向标志（DF = 0，字符串操作从低地址到高地址）
    /// 操作码: 0xFC
    fn encode_cld(&self, operands: &[Operand]) -> Result<Vec<u8>> {
        if !operands.is_empty() {
            return Err(AssemblerError::OperandError {
                message: "清除方向 指令不需要操作数".to_string(),
                line: 0,
            });
        }
        Ok(vec![0xFC])
    }

    /// 编码设置方向标志指令（STD）
    /// 
    /// STD: 设置方向标志（DF = 1，字符串操作从高地址到低地址）
    /// 操作码: 0xFD
    fn encode_std(&self, operands: &[Operand]) -> Result<Vec<u8>> {
        if !operands.is_empty() {
            return Err(AssemblerError::OperandError {
                message: "设置方向 指令不需要操作数".to_string(),
                line: 0,
            });
        }
        Ok(vec![0xFD])
    }

    /// 编码清除中断标志指令（CLI）
    /// 
    /// CLI: 清除中断标志（IF = 0，禁止可屏蔽中断）
    /// 操作码: 0xFA
    fn encode_cli(&self, operands: &[Operand]) -> Result<Vec<u8>> {
        if !operands.is_empty() {
            return Err(AssemblerError::OperandError {
                message: "清除中断 指令不需要操作数".to_string(),
                line: 0,
            });
        }
        Ok(vec![0xFA])
    }

    /// 编码设置中断标志指令（STI）
    /// 
    /// STI: 设置中断标志（IF = 1，允许可屏蔽中断）
    /// 操作码: 0xFB
    fn encode_sti(&self, operands: &[Operand]) -> Result<Vec<u8>> {
        if !operands.is_empty() {
            return Err(AssemblerError::OperandError {
                message: "设置中断 指令不需要操作数".to_string(),
                line: 0,
            });
        }
        Ok(vec![0xFB])
    }

    /// 编码输入指令（IN）
    /// 
    /// IN: 从I/O端口读取数据到累加器
    /// 支持两种格式：
    /// 1. IN AX, imm8 - 直接端口寻址（8位端口号）
    /// 2. IN AX, DX - 间接端口寻址（16位端口号在DX中）
    fn encode_in(&self, operands: &[Operand]) -> Result<Vec<u8>> {
        if operands.len() != 2 {
            return Err(AssemblerError::OperandError {
                message: "输入 指令需要 2 个操作数".to_string(),
                line: 0,
            });
        }

        let (dst, src) = (&operands[0], &operands[1]);
        
        // 检查目标操作数必须是累加器（AX）
        if let Operand::Register(reg) = dst {
            if reg != "累加器" {
                return Err(AssemblerError::OperandError {
                    message: "输入 指令的目标操作数必须是累加器".to_string(),
                    line: 0,
                });
            }
        } else {
            return Err(AssemblerError::OperandError {
                message: "输入 指令的目标操作数必须是寄存器".to_string(),
                line: 0,
            });
        }

        match src {
            // IN AX, imm8
            Operand::Immediate(port) => {
                if *port < 0 || *port > 255 {
                    return Err(AssemblerError::OperandError {
                        message: format!("端口号必须在 0-255 范围内，得到: {}", port),
                        line: 0,
                    });
                }
                // IN AX, imm8 = E5 + imm8 (2字节)
                Ok(vec![0xE5, *port as u8])
            }
            // IN AX, DX
            Operand::Register(reg) if reg == "数据" => {
                // IN AX, DX = ED (1字节)
                Ok(vec![0xED])
            }
            _ => Err(AssemblerError::OperandError {
                message: "输入 指令的源操作数必须是立即数（端口号）或数据寄存器（DX）".to_string(),
                line: 0,
            }),
        }
    }

    /// 编码输出指令（OUT）
    /// 
    /// OUT: 将累加器的数据写入I/O端口
    /// 支持两种格式：
    /// 1. OUT imm8, AX - 直接端口寻址（8位端口号）
    /// 2. OUT DX, AX - 间接端口寻址（16位端口号在DX中）
    fn encode_out(&self, operands: &[Operand]) -> Result<Vec<u8>> {
        if operands.len() != 2 {
            return Err(AssemblerError::OperandError {
                message: "输出 指令需要 2 个操作数".to_string(),
                line: 0,
            });
        }

        let (src, dst) = (&operands[0], &operands[1]);
        
        // 检查源操作数（端口）
        let (port_imm, is_dx) = match src {
            Operand::Immediate(port) => {
                if *port < 0 || *port > 255 {
                    return Err(AssemblerError::OperandError {
                        message: format!("端口号必须在 0-255 范围内，得到: {}", port),
                        line: 0,
                    });
                }
                (Some(*port as u8), false)
            }
            Operand::Register(reg) if reg == "数据" => {
                (None, true)
            }
            _ => {
                return Err(AssemblerError::OperandError {
                    message: "输出 指令的源操作数必须是立即数（端口号）或数据寄存器（DX）".to_string(),
                    line: 0,
                });
            }
        };

        // 检查目标操作数必须是累加器（AX）
        if let Operand::Register(reg) = dst {
            if reg != "累加器" {
                return Err(AssemblerError::OperandError {
                    message: "输出 指令的目标操作数必须是累加器".to_string(),
                    line: 0,
                });
            }
        } else {
            return Err(AssemblerError::OperandError {
                message: "输出 指令的目标操作数必须是寄存器".to_string(),
                line: 0,
            });
        }

        if is_dx {
            // OUT DX, AX = EF (1字节)
            Ok(vec![0xEF])
        } else {
            // OUT imm8, AX = E7 + imm8 (2字节)
            Ok(vec![0xE7, port_imm.unwrap()])
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_encode_nop() {
        let encoder = OtherEncoder;
        let operands = vec![];
        let result = encoder.encode("空操作", &operands).unwrap();
        // NOP = 90
        assert_eq!(result, vec![0x90]);
    }

    #[test]
    fn test_encode_int() {
        let encoder = OtherEncoder;
        let operands = vec![Operand::Immediate(0x21)];
        let result = encoder.encode("中断", &operands).unwrap();
        // INT 21h = CD 21
        assert_eq!(result, vec![0xCD, 0x21]);
    }

    #[test]
    fn test_encode_int_invalid_range() {
        let encoder = OtherEncoder;
        let operands = vec![Operand::Immediate(256)];
        let result = encoder.encode("中断", &operands);
        assert!(result.is_err());
    }

    #[test]
    fn test_encode_nop_with_operands() {
        let encoder = OtherEncoder;
        let operands = vec![Operand::Immediate(10)];
        let result = encoder.encode("空操作", &operands);
        assert!(result.is_err());
    }

    // 标志位操作指令测试
    #[test]
    fn test_encode_clc() {
        let encoder = OtherEncoder;
        let operands = vec![];
        let result = encoder.encode("清除进位", &operands).unwrap();
        // CLC = F8
        assert_eq!(result, vec![0xF8]);
    }

    #[test]
    fn test_encode_stc() {
        let encoder = OtherEncoder;
        let operands = vec![];
        let result = encoder.encode("设置进位", &operands).unwrap();
        // STC = F9
        assert_eq!(result, vec![0xF9]);
    }

    #[test]
    fn test_encode_cmc() {
        let encoder = OtherEncoder;
        let operands = vec![];
        let result = encoder.encode("取反进位", &operands).unwrap();
        // CMC = F5
        assert_eq!(result, vec![0xF5]);
    }

    #[test]
    fn test_encode_cld() {
        let encoder = OtherEncoder;
        let operands = vec![];
        let result = encoder.encode("清除方向", &operands).unwrap();
        // CLD = FC
        assert_eq!(result, vec![0xFC]);
    }

    #[test]
    fn test_encode_std() {
        let encoder = OtherEncoder;
        let operands = vec![];
        let result = encoder.encode("设置方向", &operands).unwrap();
        // STD = FD
        assert_eq!(result, vec![0xFD]);
    }

    #[test]
    fn test_encode_cli() {
        let encoder = OtherEncoder;
        let operands = vec![];
        let result = encoder.encode("清除中断", &operands).unwrap();
        // CLI = FA
        assert_eq!(result, vec![0xFA]);
    }

    #[test]
    fn test_encode_sti() {
        let encoder = OtherEncoder;
        let operands = vec![];
        let result = encoder.encode("设置中断", &operands).unwrap();
        // STI = FB
        assert_eq!(result, vec![0xFB]);
    }

    #[test]
    fn test_encode_flag_with_operands() {
        let encoder = OtherEncoder;
        let operands = vec![Operand::Immediate(10)];
        // 所有标志位操作指令都不应该接受操作数
        assert!(encoder.encode("清除进位", &operands).is_err());
        assert!(encoder.encode("设置进位", &operands).is_err());
        assert!(encoder.encode("取反进位", &operands).is_err());
        assert!(encoder.encode("清除方向", &operands).is_err());
        assert!(encoder.encode("设置方向", &operands).is_err());
        assert!(encoder.encode("清除中断", &operands).is_err());
        assert!(encoder.encode("设置中断", &operands).is_err());
    }

    // IO指令测试
    #[test]
    fn test_encode_in_immediate() {
        let encoder = OtherEncoder;
        let operands = vec![
            Operand::Register("累加器".to_string()),
            Operand::Immediate(0x60),
        ];
        let result = encoder.encode("输入", &operands).unwrap();
        // IN AX, 60h = E5 60
        assert_eq!(result, vec![0xE5, 0x60]);
    }

    #[test]
    fn test_encode_in_dx() {
        let encoder = OtherEncoder;
        let operands = vec![
            Operand::Register("累加器".to_string()),
            Operand::Register("数据".to_string()),
        ];
        let result = encoder.encode("输入", &operands).unwrap();
        // IN AX, DX = ED
        assert_eq!(result, vec![0xED]);
    }

    #[test]
    fn test_encode_out_immediate() {
        let encoder = OtherEncoder;
        let operands = vec![
            Operand::Immediate(0x60),
            Operand::Register("累加器".to_string()),
        ];
        let result = encoder.encode("输出", &operands).unwrap();
        // OUT 60h, AX = E7 60
        assert_eq!(result, vec![0xE7, 0x60]);
    }

    #[test]
    fn test_encode_out_dx() {
        let encoder = OtherEncoder;
        let operands = vec![
            Operand::Register("数据".to_string()),
            Operand::Register("累加器".to_string()),
        ];
        let result = encoder.encode("输出", &operands).unwrap();
        // OUT DX, AX = EF
        assert_eq!(result, vec![0xEF]);
    }

    #[test]
    fn test_encode_in_invalid_port() {
        let encoder = OtherEncoder;
        let operands = vec![
            Operand::Register("累加器".to_string()),
            Operand::Immediate(256),
        ];
        let result = encoder.encode("输入", &operands);
        assert!(result.is_err());
    }

    #[test]
    fn test_encode_in_wrong_register() {
        let encoder = OtherEncoder;
        let operands = vec![
            Operand::Register("基址".to_string()),
            Operand::Immediate(0x60),
        ];
        let result = encoder.encode("输入", &operands);
        assert!(result.is_err());
    }

    #[test]
    fn test_encode_out_invalid_port() {
        let encoder = OtherEncoder;
        let operands = vec![
            Operand::Immediate(256),
            Operand::Register("累加器".to_string()),
        ];
        let result = encoder.encode("输出", &operands);
        assert!(result.is_err());
    }

    #[test]
    fn test_encode_out_wrong_register() {
        let encoder = OtherEncoder;
        let operands = vec![
            Operand::Immediate(0x60),
            Operand::Register("基址".to_string()),
        ];
        let result = encoder.encode("输出", &operands);
        assert!(result.is_err());
    }
}

